<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JSON格式化工具</title>
    <link rel="icon" type="image/x-icon" href="/tools/static/favicon.ico">
    <style>
        body {
            font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
            max-width: 1400px;
            margin: 0 auto;
            padding: 40px 20px;
            background: linear-gradient(135deg, #e0e7ff 0%, #f3e8ff 100%);
            color: #1f2937;
            position: relative;
        }
        h1 {
            color: #1e40af;
            text-align: center;
            margin-bottom: 40px;
            font-size: 2.5rem;
            font-weight: 700;
            text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
        }
        .container {
            display: flex;
            gap: 30px;
            background: #ffffff;
            border-radius: 16px;
            padding: 30px;
            box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
            backdrop-filter: blur(10px);
            background: rgba(255, 255, 255, 0.95);
        }
        .input-area, .output-area {
            flex: 1;
            position: relative;
        }
        h3 {
            color: #374151;
            margin-bottom: 15px;
            font-size: 1.25rem;
            font-weight: 600;
        }
        .textarea-wrapper {
            position: relative;
            background: #f9fafb;
            border-radius: 12px;
            border: 1px solid #d1d5db;
            transition: border-color 0.3s ease;
        }
        .textarea-wrapper:hover {
            border-color: #93c5fd;
        }
        textarea {
            width: 100%;
            height: 500px;
            padding: 15px;
            font-family: 'Fira Code', 'Courier New', monospace;
            font-size: 14px;
            font-weight: 500;
            line-height: 1.6;
            border: none;
            resize: vertical;
            box-sizing: border-box;
            background: transparent;
            color: #1f2937;
        }
        textarea:focus {
            outline: none;
            box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
        }
        .output-container {
            position: relative;
            height: 500px;
            overflow-y: auto;
            font-family: 'Fira Code', 'Courier New', monospace;
            font-size: 14px;
            font-weight: 500;
            line-height: 1.6;
            padding: 15px;
            background: #f9fafb;
            border-radius: 12px;
            border: 1px solid #d1d5db;
            transition: border-color 0.3s ease;
            box-sizing: border-box;
        }
        .output-container:hover {
            border-color: #93c5fd;
        }
        /* 自动换行样式 */
        .output-container.wrap {
            overflow-x: hidden; /* 隐藏横向滚动条 */
        }
        .output-container.wrap .line-content {
            white-space: pre-wrap; /* 保留换行和空格，同时允许自动换行 */
            word-break: break-all; /* 强制长单词或连续字符换行 */
        }
        /* 不换行，显示横向滚动条样式 */
        .output-container.no-wrap {
            overflow-x: auto; /* 显示横向滚动条 */
        }
        .output-container.no-wrap .line-content {
            white-space: pre; /* 保留原始格式，不自动换行 */
        }
        .line {
            display: flex;
            align-items: flex-start; /* 确保长文本换行时对齐 */
        }
        .line-number {
            width: 40px;
            color: #6b7280;
            text-align: right;
            padding-right: 10px;
            border-right: 1px solid #e5e7eb;
            user-select: none;
            flex-shrink: 0; /* 防止行号被压缩 */
        }
        .line-content {
            flex: 1;
            padding-left: 15px;
            color: #1f2937;
        }
        .buttons {
            margin: 20px 0;
            display: flex;
            gap: 12px;
            justify-content: center;
        }
        button {
            padding: 12px 24px;
            cursor: pointer;
            background: linear-gradient(45deg, #3b82f6, #2563eb);
            color: white;
            border: none;
            border-radius: 8px;
            font-size: 14px;
            font-weight: 600;
            transition: transform 0.2s, box-shadow 0.3s, background 0.3s;
        }
        button:hover {
            transform: translateY(-2px);
            box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
            background: linear-gradient(45deg, #60a5fa, #3b82f6);
        }
        .copy-btn {
            background: linear-gradient(45deg, #34d399, #10b981);
        }
        .copy-btn:hover {
            background: linear-gradient(45deg, #6ee7b7, #34d399);
        }
        .error {
            color: #dc2626;
            margin-top: 10px;
            padding: 10px;
            background: #fee2e2;
            border-radius: 8px;
            display: none;
            font-size: 14px;
        }
        /* 自定义提示框样式 */
        .toast {
            position: fixed;
            top: 20px;
            right: 20px;
            background: #10b981;
            color: white;
            padding: 12px 24px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
            display: flex;
            align-items: center;
            gap: 10px;
            font-size: 14px;
            font-weight: 500;
            opacity: 0;
            transform: translateY(-20px);
            transition: opacity 0.3s ease, transform 0.3s ease;
            z-index: 1000;
        }
        .toast.show {
            opacity: 1;
            transform: translateY(0);
        }
        .toast .close-btn {
            cursor: pointer;
            font-size: 16px;
            line-height: 1;
            color: #d1fae5;
        }
        .toast .close-btn:hover {
            color: #ffffff;
        }
        /* 复选框样式 */
        .checkbox-wrapper {
            margin-bottom: 10px;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .checkbox-wrapper label {
            font-size: 14px;
            color: #374151;
            cursor: pointer;
        }
        .checkbox-wrapper input[type="checkbox"] {
            width: 16px;
            height: 16px;
            cursor: pointer;
        }
    </style>
</head>
<body>
<h1>JSON格式化工具</h1>
<div class="container">
    <div class="input-area">
        <h3>输入JSON</h3>
        <div class="textarea-wrapper">
            <textarea id="inputJson" placeholder="在此输入JSON内容..."></textarea>
        </div>
        <div class="buttons">
            <button onclick="formatJson()">格式化</button>
            <button onclick="compressJson()">压缩</button>
            <button onclick="clearInput()">清空</button>
        </div>
        <div id="errorMsg" class="error"></div>
    </div>
    <div class="output-area">
        <h3>输出结果</h3>
        <div id="outputJson" class="output-container wrap"></div>
        <div class="buttons">
            <button class="copy-btn" onclick="copyOutput()">复制</button>
        </div>
    </div>
</div>

<script>
    let jsonData = null;


    function formatJson() {
        const input = document.getElementById('inputJson').value;
        const output = document.getElementById('outputJson');
        const errorMsg = document.getElementById('errorMsg');

        try {
            jsonData = JSON.parse(input);
            renderJson();
            errorMsg.style.display = 'none';
        } catch (e) {
            errorMsg.textContent = '错误：无效的JSON格式 - ' + e.message;
            errorMsg.style.display = 'block';
            output.innerHTML = '';
        }
    }

    function compressJson() {
        const input = document.getElementById('inputJson').value;
        const output = document.getElementById('outputJson');
        const errorMsg = document.getElementById('errorMsg');

        try {
            const jsonObj = JSON.parse(input);
            output.innerHTML = '';
            const line = document.createElement('div');
            line.className = 'line';
            line.innerHTML = `
                    <span class="line-number">1</span>
                    <span class="line-content">${JSON.stringify(jsonObj)}</span>
                `;
            output.appendChild(line);
            errorMsg.style.display = 'none';
        } catch (e) {
            errorMsg.textContent = '错误：无效的JSON格式 - ' + e.message;
            errorMsg.style.display = 'block';
            output.innerHTML = '';
        }
    }

    function clearInput() {
        document.getElementById('inputJson').value = '';
        document.getElementById('outputJson').innerHTML = '';
        document.getElementById('errorMsg').style.display = 'none';
    }

    function copyOutput() {
        const output = document.getElementById('outputJson');
        const text = Array.from(output.querySelectorAll('.line-content'))
            .map(span => span.textContent)
            .join('\n');

        // 降级方案：使用 textarea 和 document.execCommand 复制文本
        const unsecuredCopyToClipboard = (text) => {
            const textArea = document.createElement('textarea');
            textArea.value = text;
            textArea.style.position = 'fixed'; // 避免影响页面布局
            textArea.style.opacity = '0'; // 隐藏 textarea
            document.body.appendChild(textArea);
            textArea.focus();
            textArea.select();
            try {
                const successful = document.execCommand('copy');
                if (successful) {
                    showToast('已复制到剪贴板', 'success');
                } else {
                    showToast('复制失败，请手动复制', 'error');
                }
            } catch (err) {
                showToast('复制失败：' + err.message, 'error');
            }
            document.body.removeChild(textArea);
        };

        // 直接使用降级方案
        unsecuredCopyToClipboard(text);
    }

    function renderJson() {
        if (!jsonData) return;
        const output = document.getElementById('outputJson');
        output.innerHTML = '';
        let lineNumber = 1;
        lineNumber = renderJsonNode(jsonData, 0, output, lineNumber).lineNumber;
    }

    function renderJsonNode(node, indent = 0, container, lineNumber) {
        const indentStr = '  '.repeat(indent);
        let currentLine = lineNumber;

        if (Array.isArray(node)) {
            const line = document.createElement('div');
            line.className = 'line';
            line.innerHTML = `
                    <span class="line-number">${currentLine}</span>
                    <span class="line-content">${indentStr}[</span>
                `;
            container.appendChild(line);
            currentLine++;

            node.forEach((item, index) => {
                const result = renderJsonNode(item, indent + 1, container, currentLine);
                currentLine = result.lineNumber;
                if (index < node.length - 1) {
                    const commaLine = document.createElement('div');
                    commaLine.className = 'line';
                    commaLine.innerHTML = `
                            <span class="line-number">${currentLine}</span>
                            <span class="line-content">${indentStr}  ,</span>
                        `;
                    container.appendChild(commaLine);
                    currentLine++;
                }
            });

            const closingLine = document.createElement('div');
            closingLine.className = 'line';
            closingLine.innerHTML = `
                    <span class="line-number">${currentLine}</span>
                    <span class="line-content">${indentStr}]</span>
                `;
            container.appendChild(closingLine);
            currentLine++;
        } else if (typeof node === 'object' && node !== null) {
            const line = document.createElement('div');
            line.className = 'line';
            line.innerHTML = `
                    <span class="line-number">${currentLine}</span>
                    <span class="line-content">${indentStr}{</span>
                `;
            container.appendChild(line);
            currentLine++;

            const keys = Object.keys(node);
            keys.forEach((key, index) => {
                const value = node[key];
                if (typeof value === 'object' && value !== null) {
                    const keyLine = document.createElement('div');
                    keyLine.className = 'line';
                    keyLine.innerHTML = `
                            <span class="line-number">${currentLine}</span>
                            <span class="line-content">${indentStr}  "${key}": </span>
                        `;
                    container.appendChild(keyLine);
                    currentLine++;
                    const result = renderJsonNode(value, indent + 1, container, currentLine);
                    currentLine = result.lineNumber;
                } else {
                    const keyLine = document.createElement('div');
                    keyLine.className = 'line';
                    keyLine.innerHTML = `
                            <span class="line-number">${currentLine}</span>
                            <span class="line-content">${indentStr}  "${key}": ${JSON.stringify(value)}</span>
                        `;
                    container.appendChild(keyLine);
                    currentLine++;
                }
                if (index < keys.length - 1) {
                    const commaLine = document.createElement('div');
                    commaLine.className = 'line';
                    commaLine.innerHTML = `
                            <span class="line-number">${currentLine}</span>
                            <span class="line-content">${indentStr}  ,</span>
                        `;
                    container.appendChild(commaLine);
                    currentLine++;
                }
            });

            const closingLine = document.createElement('div');
            closingLine.className = 'line';
            closingLine.innerHTML = `
                    <span class="line-number">${currentLine}</span>
                    <span class="line-content">${indentStr}}</span>
                `;
            container.appendChild(closingLine);
            currentLine++;
        } else {
            const line = document.createElement('div');
            line.className = 'line';
            line.innerHTML = `
                    <span class="line-number">${currentLine}</span>
                    <span class="line-content">${indentStr}${JSON.stringify(node)}</span>
                `;
            container.appendChild(line);
            currentLine++;
        }
        return { lineNumber: currentLine };
    }

    // 自定义提示框函数
    function showToast(message) {
        // 如果已有提示框，先移除
        const existingToast = document.querySelector('.toast');
        if (existingToast) {
            existingToast.remove();
        }

        // 创建提示框
        const toast = document.createElement('div');
        toast.className = 'toast';
        toast.innerHTML = `
                <span>${message}</span>
                <span class="close-btn">×</span>
            `;
        document.body.appendChild(toast);

        // 显示提示框
        setTimeout(() => {
            toast.classList.add('show');
        }, 10);

        // 自动关闭
        const autoCloseTimeout = setTimeout(() => {
            toast.classList.remove('show');
            setTimeout(() => {
                toast.remove();
            }, 300);
        }, 3000);

        // 点击关闭按钮
        toast.querySelector('.close-btn').addEventListener('click', () => {
            clearTimeout(autoCloseTimeout);
            toast.classList.remove('show');
            setTimeout(() => {
                toast.remove();
            }, 300);
        });
    }
</script>
</body>
</html>